/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package kuti.grp5;

import java.awt.geom.Point2D;
import static java.lang.Math.sqrt;

/**
 *
 * The class Vector2D is used to manipulate vector in a plane has two
 * dimentions, where the axes are orthogonal.
 *
 * @author Marc Schorderet <marc.schorderet@edu.hefr.ch>
 *
 *
 */

public final class Vector2D {

    /*
     * The value x is used to represent the movement on the X axis (horizontal
     * axis)
     */
    private double x;
    /*
     * The value y is used to represent the movement on the Y axis (vertical
     * axis)
     */
    private double y;

    /**
     * The constructor creates a vector from the x and y values.
     *
     * @param x Move arround the x axis
     * @param y Move arround the y axis
     *
     */
    public Vector2D(double x, double y) {

        if(Double.isNaN(x) || Double.isNaN(y)){
            throw new IllegalArgumentException("The point value is not valid. [not a number]");
        }
        
        this.x = x;
        this.y = y;

    }

    /**
     * The constructor creates an vector from the origine to the point.
     *
     * @param p end point of the vector (begining on the origin)
     *
     */
    public Vector2D(Point2D p) {
        
        if(p == null){
            throw new IllegalArgumentException("The point is null.");
        }
        
        if(Double.isNaN(p.getX()) || Double.isNaN(p.getY())){
            throw new IllegalArgumentException("The point value is not valid. [not a number]");
        }
        
        this.x = p.getX();
        this.y = p.getY();

    }

    /**
     * The constructor creates a vector from the x and y values and strech it
     * witch the factor.
     *
     * @param x Move arround the x axis
     * @param y Move arround the y axis
     * @param factor Strech factor
     *
     */
    public Vector2D(double x, double y, double factor) {

        // Call the other constructor with the x and y moves
        this(x, y);
        
        // Streching
        stretch(factor);

    }

    /**
     * The constructor creates a vector from the start and the end point.
     *
     * @param start Start ponit of the vector
     * @param end End point of the vector.
     *
     */
    public Vector2D(Point2D start, Point2D end) {
        
        if(start == null || end == null){
            throw new IllegalArgumentException("The point is null.");
        }
        
        if(Double.isNaN(start.getX()) || Double.isNaN(start.getY())){
            throw new IllegalArgumentException("The point value is not valid. [not a number]");
        }
        
        if(Double.isNaN(end.getX()) || Double.isNaN(end.getY())){
            throw new IllegalArgumentException("The point value is not valid. [not a number]");
        }
        
        // Call the other constructor with the x and y moves
        this.x = end.getX() - start.getX();
        this.y = end.getY() - start.getY();

    }

    /**
     * This method returns the x value.
     *
     * @return Returns the x value.
     *
     */
    public double getX() {

        return this.x;

    }

    /**
     * This method returns the y value.
     *
     * @return Returns the y value.
     *
     */
    public double getY() {

        return this.y;

    }

    /**
     * This methods returns the multiplication of two vectors.
     *
     * @param other Other vector
     *
     * @return Returns the multiplication of two vectors.
     *
     */
    public double multi(Vector2D other) {
        
        if(other == null){
            throw new IllegalArgumentException("The vector is not defined.");
        }
        
        return this.x * other.x + this.y * other.y;

    }

    /**
     * This method returns the length of the vector.
     *
     * @return Returns the length of the vector.
     *
     */
    public double length() {

        return sqrt(x * x + y * y);

    }


    /**
     * This method adds two vectors
     *
     * @param other The other vector
     *
     */
    public void add(Vector2D other) {

        this.x += other.x;
        this.y += other.y;

    }

    /**
     * This method subtracts two vectors
     *
     * @param other The other vector
     *
     */
    public void sub(Vector2D other) {

        if(other == null){
            throw new IllegalArgumentException("The vector are not defined.");
        }
        
        this.x -= other.x;
        this.y -= other.y;

    }

    /**
     * This method invert the vector
     *
     */
    public void invert() {

        stretch(-1);

    }

    /**
     * This method strech the vector witch a factor.
     *
     * @param alpha strech factor of the vector
     *
     */
    public void stretch(double alpha) {
        
        if(Double.isNaN(alpha)){
            throw new IllegalArgumentException("The factor is not a number");
        }
        
        this.x *= alpha;
        this.y *= alpha;

    }

    /**
     * This method test if this vector is zero (0, 0)
     *
     * @return Returns true if the vector is zero (0,0), else false.
     */
    public boolean isZero() {

        return (this.x == 0) && (this.y == 0);

    }

    /**
     * This method calculate the scalar product of two vectors.
     *
     * @param v1 First vector
     * @param v2 Second vector
     *
     * @return Returns the scalar product of the two vectors
     *
     */
    public static double scalarProduct(Vector2D v1, Vector2D v2) {
        
        if(v1 == null || v2 == null){
            throw new IllegalArgumentException("The vectors are not defined.");
        }
        
        // if one vector is zero, the result is zero.
        if (v1.isZero() || v2.isZero()) {
            return 0.0;
        }

        // Calculate the cosinus of the internal angle
        double cosPhi = (v1.x * v2.x + v1.y * v2.y) / (v1.length() * v2.length());

        return v1.length() * v2.length() * cosPhi;

    }

    /**
     * This method return the string representation of the vector.
     *
     * Sample : Vector2D : [3.2][-2.0]
     *
     * @return Returns the string representation of the vector.
     *
     */
    @Override
    public String toString() {
        return "Vector2D : [" + this.x + "][" + this.y + "]";
    }

    /**
     * This method test if two vectors are orthogonal. Two vectors are
     * orthogonal if the scalar product is zero. (perpendicular)
     *
     * @param v1 First vector
     * @param v2 Second vector
     *
     * @return Returns true if the vectors are orthoginal, else false.
     *
     */
    public static boolean isOrthogonal(Vector2D v1, Vector2D v2) {
        
        if(v1 == null || v2 == null){
            throw new IllegalArgumentException("The vectors are not defined.");
        }
        
        return scalarProduct(v1, v2) == 0;

    }

    /**
     * This methord create a projection vecor from two vectors. Attention, the
     * order of the vectors is significant. The first vector (v2) is projected
     * of the second vector (v2).
     *
     * The result vector is parallel to the first vector (v1)
     *
     * @param v1 First vector
     * @param v2 Second vector
     *
     * @return Returns the projection from v2 to v1.
     *
     */
    public static Vector2D projection(Vector2D v1, Vector2D v2) {
        
        if(v1 == null || v2 == null){
            throw new IllegalArgumentException("The vectors are not defined.");
        }
        
        // Strech factor
        double factor = (v1.x * v2.x + v1.y * v2.y) / (v1.length()* v1.length());

        // Basis vector (witch direction)
        Vector2D vector = v1.clone();

        try{
            //Streching of the vector
            vector.stretch(factor);
        }catch(Exception e){}

        return vector;

    }

    /**
     * This method return a new Vector2D cloned from this one.
     *
     * @return Returns the cloned vector.
     *
     */
    @Override
    public Vector2D clone() {

        return new Vector2D(this.x, this.y);

    }

    /**
     * This method convert the vector to an Point2D. The point has the x and y
     * values from the vector.
     *
     * @return Returns a point2d with x and y values from the vector.
     *
     */
    public Point2D getPoint() {

        return new Point2D.Double(x, y);

    }

    /**
     * This method normalizes a vector.
     * vX / (vX^2 + vY^2)^(1/2).
     * 
     * If the vector is zero. This method generete an ArithmeticException.
     */
    
    public void normalizeVector() throws ArithmeticException {
        double div = length();
        
        if(div == 0.0){
            throw new ArithmeticException("/ by zero");
        }
        
        x /= div;
        y /= div;
    }

    /**
     * This method translate a point with the two component of a vector.
     * 
     * @param p     point that will be translated
     * 
     * @return new point translated with the vector
     */
    
    public Point2D tranlateVector(Point2D p) {
        
        return new Point2D.Double(p.getX() + this.x, p.getY() + this.y);

    }
}
